/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
#include "JoystickControlComponent.h"

OptionValues::OptionValues(const std::string optName)
  : optionName (optName),
    optionValue (0),
    optionChanged (false)
{}

ComponentConfiguration::ComponentConfiguration()
  : optionsMutex (ownMutex_Init()),
    axisOptions (),
    buttonOptions (),
    joysticksMutex (ownMutex_Init()),
    joysticks ()
{}

ComponentConfiguration::~ComponentConfiguration()
{
  if (optionsMutex)
    ownMutex_Destroy(optionsMutex);
  
  if (joysticksMutex)
    ownMutex_Destroy(joysticksMutex);
}

void ComponentConfiguration::handleNewEvent(const GIMUIJoystickEvent &newEvent)
{
  // Update the value on option for changed axis/button.
  
  wxEventType eventType = newEvent.GetEventType();
  int optionId = -1;
  
  if (eventType == wxEVT_GIMUI_JOYSTICK_MOVE) { // Movement event.
    // Find which option this joystick/axis combination controls.
    ownMutex_Lock(joysticksMutex);
    t_joystickStatusListConstIterator joyIter =
        joysticks.find(newEvent.getJoystickId());
    if (joyIter != joysticks.end()) { // Joystick found.
      t_changeIdReferencesListConstIterator axisIter =
          joyIter->second.axes.find(newEvent.getChangeId());
      if (axisIter != joyIter->second.axes.end()) { // Axis found.
        optionId = axisIter->second;
      }
    }
    
    // If option was found, update it's value and mark changed to true.
    if (optionId != -1) {
      ownMutex_Lock(optionsMutex);
      t_optionContainerIterator aOptIter = axisOptions.find(optionId);
      if (aOptIter != axisOptions.end()) {
        aOptIter->second.optionValue =
            newEvent.currentStatus.getPosition(newEvent.getChangeId());
        aOptIter->second.optionChanged = true;
      } else { // OptionId not found. Internal error!!
        //TODO Something?
        dPrint(1, "Did not find specified optionId %d, ERROR!", optionId);
      }
      ownMutex_Unlock(optionsMutex);
    } else {
      dPrint(12, "Joystick %d, axis %d not set to any option.",
             newEvent.getJoystickId(), newEvent.getChangeId());
    }
    ownMutex_Unlock(joysticksMutex);
    
  } else if (eventType == wxEVT_GIMUI_JOYSTICK_BUTTON_DOWN ||
             eventType == wxEVT_GIMUI_JOYSTICK_BUTTON_UP) // Button event.
  {
    // Find which option this joystick/button combination controls.
    ownMutex_Lock(joysticksMutex);
    t_joystickStatusListConstIterator joyIter =
        joysticks.find(newEvent.getJoystickId());
    if (joyIter != joysticks.end()) { // Joystick found.
      t_changeIdReferencesListConstIterator buttonIter =
          joyIter->second.buttons.find(newEvent.getChangeId());
      if (buttonIter != joyIter->second.buttons.end()) { // Axis found.
        optionId = buttonIter->second;
      }
    }
    
    // If option was found, update it's value and mark changed to true.
    if (optionId != -1) {
      ownMutex_Lock(optionsMutex);
      t_optionContainerIterator bOptIter = buttonOptions.find(optionId);
      if (bOptIter != buttonOptions.end()) {
        if (eventType == wxEVT_GIMUI_JOYSTICK_BUTTON_DOWN) { // Button down
          bOptIter->second.optionValue = 1.0;
          bOptIter->second.optionChanged = true;
        } else { // Button up
          bOptIter->second.optionValue = 0;
          bOptIter->second.optionChanged = true;
        }
      } else { // OptionId not found. Internal error!!
        //TODO Something?
        dPrint(1, "Did not find specified optionId %d, ERROR!", optionId);
      }
      ownMutex_Unlock(optionsMutex);
    } else {
      /*
      dPrint(6, "Joystick %d, button %d not set to any option.",
             newEvent.getJoystickId(), newEvent.getChangeId());
      */
    }
    ownMutex_Unlock(joysticksMutex);
    
  } else { // Unknown event
    dPrint(1, "Got unknown event.");
  }
  
}
    
int ComponentConfiguration::listAxisOptions(t_optionList &options) const
{
  int n = 0;
  ownMutex_Lock(optionsMutex);
  for (t_optionContainerConstIterator cIter = axisOptions.begin();
       cIter != axisOptions.end();
       ++cIter)
  {
    options.push_back(t_selectOptionsPair(cIter->first, cIter->second.optionName));
    ++n;
  }
  ownMutex_Unlock(optionsMutex);
  return n;
}
    
int ComponentConfiguration::listButtonOptions(t_optionList &options) const
{
  int n = 0;
  ownMutex_Lock(optionsMutex);
  for (t_optionContainerConstIterator cIter = buttonOptions.begin();
       cIter != buttonOptions.end();
       ++cIter)
  {
    options.push_back(t_selectOptionsPair(cIter->first, cIter->second.optionName));
    ++n;
  }
  ownMutex_Unlock(optionsMutex);  
  return n;
}

int ComponentConfiguration::setAxisOption(const int optionId,
                                          const int joystickId,
                                          const int axisId)
{
  dPrint(2, "Setting option %d to read axis %d in joystick %d", optionId,
         axisId, joystickId);
  int result = JOYSTICKCONTROL_OK;
  int oldOptionId = -1;
  ownMutex_Lock(joysticksMutex);
  
  // Find joystick
  t_joystickStatusListIterator joyIter = joysticks.find(joystickId);
  if (joyIter == joysticks.end()) { // Joystick not found, add it.
    joyIter = (joysticks.insert(t_joystickStatusListPair(joystickId,
        ChangeIdReferences()))).first;
  }
  
  // Find axis
  t_changeIdReferencesListIterator refIter = joyIter->second.axes.find(axisId);
  if (refIter == joyIter->second.axes.end()) { // AxisId not found.
    if (optionId < 0) { // Don't add
      
    } else { // Add optionid
      joyIter->second.axes.insert(t_changeIdReferencesPair(axisId, optionId));
    }
  } else { // Axis found
    if (optionId < 0) { // Remove reference.
      oldOptionId = refIter->second;
      joyIter->second.axes.erase(refIter);
    } else { // Set optionid
      oldOptionId = refIter->second;
      refIter->second = optionId; 
    }
  }
  ownMutex_Unlock(joysticksMutex);
  
  ownMutex_Lock(optionsMutex); 
  if (optionId > -1) { // Reset axis value to neutral (0.00)
    t_optionContainerIterator aOptIter = axisOptions.find(optionId);
    if (aOptIter != axisOptions.end()) {
      aOptIter->second.optionValue = 0;
      aOptIter->second.optionChanged = true;
    } else { // Axis option not found!!
      dPrint(1, "Did not find optionId %d !!", optionId);
    }
  }
  if (oldOptionId > -1) { // Reset old axis value to neutral (0.00)
    t_optionContainerIterator aOptIter = axisOptions.find(oldOptionId);
    if (aOptIter != axisOptions.end()) {
      aOptIter->second.optionValue = 0;
      aOptIter->second.optionChanged = true;
    } else { // Axis option not found!!
      dPrint(1, "Did not find optionId %d !!", oldOptionId);
    }
  }
  
  ownMutex_Unlock(optionsMutex);
  
  return result;
}

int ComponentConfiguration::setButtonOption(const int optionId,
                                            const int joystickId,
                                            const int buttonId)
{
  dPrint(2, "Setting option %d to read button %d in joystick %d", optionId,
         buttonId, joystickId);
  int result = JOYSTICKCONTROL_OK;
  int oldOptionId = -1;
  
  ownMutex_Lock(joysticksMutex);
  // Find joystick
  t_joystickStatusListIterator joyIter = joysticks.find(joystickId);
  if (joyIter == joysticks.end()) { // Joystick not found, add it.
    joyIter = (joysticks.insert(t_joystickStatusListPair(joystickId,
        ChangeIdReferences()))).first;
  }
  // Find button
  t_changeIdReferencesListIterator refIter = joyIter->second.buttons.find(buttonId);
  if (refIter == joyIter->second.buttons.end()) { // ButtonId not found.
    if (optionId < 0) { // Do not add optionid
      
    } else { // Add optionid
      joyIter->second.buttons.insert(t_changeIdReferencesPair(buttonId, optionId));
    }
  } else { // Button found
    if (optionId < 0) { // Remove reference
      oldOptionId = refIter->second;
      joyIter->second.buttons.erase(refIter);
    } else { // Set optionid
      oldOptionId = refIter->second;
      refIter->second = optionId;
    }
  }
  ownMutex_Unlock(joysticksMutex);
  
  ownMutex_Lock(optionsMutex);
  if (optionId > -1) {// Reset button value to button up
    t_optionContainerIterator bOptIter = buttonOptions.find(optionId);
    if (bOptIter != buttonOptions.end()) {
      bOptIter->second.optionValue = 0;
      bOptIter->second.optionChanged = true;
    } else { // Button option not found!!
      dPrint(1, "Did not find optionId %d !!", optionId);
    }
  } 
  if (oldOptionId > -1) { // Reset button value to button up for old optionid.
    t_optionContainerIterator bOptIter = buttonOptions.find(oldOptionId);
    if (bOptIter != buttonOptions.end()) {
      bOptIter->second.optionValue = 0;
      bOptIter->second.optionChanged = true;
    } else { // Button option not found!!
      dPrint(1, "Did not find optionId %d !!", oldOptionId);
    }
  }
  ownMutex_Unlock(optionsMutex);
  
  return result;
}





int ComponentConfiguration::addAxisOption(const int optionId,
                                          const std::string &optionDescription)
{
  int result = JOYSTICKCONTROL_ERROR;
  ownMutex_Lock(optionsMutex);
  
  if ((axisOptions.insert(t_optionsPair(optionId,
      OptionValues(optionDescription)))).second)
  {
    // Insert success.
    dPrint(6, "Added axisoption id %d with description \"%s\"", optionId,
           optionDescription.c_str());
    result = JOYSTICKCONTROL_OK;
  } else {
    dPrint(2, "Could not add axisoption id %d with description \"%s\", id exists.",
           optionId, optionDescription.c_str());
    result = JOYSTICKCONTROL_ERROR;
  }
  
  ownMutex_Unlock(optionsMutex);
  return result;
}
                      
int ComponentConfiguration::addButtonOption(const int optionId,
                                            const std::string &optionDescription)
{
  int result = JOYSTICKCONTROL_ERROR;
  ownMutex_Lock(optionsMutex);
  
  if ((buttonOptions.insert(t_optionsPair(optionId,
      OptionValues(optionDescription)))).second)
  {
    // Insert success.
    dPrint(6, "Added buttonoption id %d with description \"%s\"", optionId,
           optionDescription.c_str());
    result = JOYSTICKCONTROL_OK;
  } else {
    dPrint(2, "Could not add buttonoption id %d with description \"%s\", id exists.",
           optionId, optionDescription.c_str());
    result = JOYSTICKCONTROL_ERROR;
  }
  
  ownMutex_Unlock(optionsMutex);
  return result;
}

int ComponentConfiguration::readButtonOption(const int optionId,
                                             bool *value)
{
  int returnValue = JOYSTICKCONTROL_ERROR;
  if (value != NULL) {
    ownMutex_Lock(optionsMutex);
    t_optionContainerIterator bOptIter = buttonOptions.find(optionId);
    if (bOptIter != buttonOptions.end()) {
      if (bOptIter->second.optionChanged) {
        returnValue = JOYSTICKCONTROL_STATUS_CHANGED;
        bOptIter->second.optionChanged = false;
        if (bOptIter->second.optionValue != 0) {
          *value =true; // Button down
        } else {
          *value = false; // Button up
        }
      } else {
        if (bOptIter->second.optionValue != 0) {
          *value =true; // Button down
        } else {
          *value = false; // Button up
        }
        returnValue = JOYSTICKCONTROL_STATUS_UNCHANGED;
      }
    } else { // Option not found.
      dPrint(1, "Could not find optionId %d", optionId);
      *value = false;
    }
    ownMutex_Unlock(optionsMutex);
  } else {
    dPrint(1, "Cannot write new value to NULL!");
  }
  return returnValue;
}
  
int ComponentConfiguration::readAxisOption(const int optionId,
                                           float *value)
{
  int returnValue = JOYSTICKCONTROL_ERROR;
  if (value != NULL) {
    ownMutex_Lock(optionsMutex);
    t_optionContainerIterator aOptIter = axisOptions.find(optionId);
    if (aOptIter != axisOptions.end()) {
      *value = aOptIter->second.optionValue; // Fetch value
      if (aOptIter->second.optionChanged) { // Reset changed status.
        returnValue = JOYSTICKCONTROL_STATUS_CHANGED;
        aOptIter->second.optionChanged = false;
      } else {
        returnValue = JOYSTICKCONTROL_STATUS_UNCHANGED;
      }
    } else { // Option not found
      dPrint(1, "Could not find optionId %d", optionId);
      *value = 0;
    }
    ownMutex_Unlock(optionsMutex);
  } else {
    dPrint(1, "Cannot write new value to NULL!");
  }
  return returnValue;
}

JoystickControlComponent::JoystickControlComponent(gimi::GIMI *aGIMIPtr,
                                                   wxSizer *aComponentSizer,
                                                   wxPanel *aComponentPanel,
                                                   const std::string aComponentName,
                                                   const int aComponentOutputType,
                                                   const int aComponentInputType)
  : iGIMIPtr(aGIMIPtr),
    componentSizer (aComponentSizer),
    componentPanel (aComponentPanel),
    activationStatus (false),
    componentName (aComponentName),
    componentOutputType (aComponentOutputType),
    componentInputType (aComponentInputType),
    iSetAxisJoystickOptions(),
    iSetButtonJoystickOptions()
{}

JoystickControlComponent::~JoystickControlComponent()
{} // Pure virtual destructor define.

void JoystickControlComponent::eventReceiver(const GIMUIJoystickEvent &event)
{
  configOptions.handleNewEvent(event);
}

std::string JoystickControlComponent::getComponentName() const
{
  return componentName;
}

int JoystickControlComponent::getComponentOutputType() const
{
  return componentOutputType;
}

int JoystickControlComponent::getComponentInputType() const
{
  return componentInputType;
}

int JoystickControlComponent::listAxisOptions(t_optionList &options) const
{
  return configOptions.listAxisOptions(options);
}
    
int JoystickControlComponent::listButtonOptions(t_optionList &options) const
{
  return configOptions.listButtonOptions(options);
}

int JoystickControlComponent::setAxisOption(const int optionId,
                                            const int joystickId,
                                            const int axisId)
{
  
  bool remove = false;
  std::vector<JoystickOption>::iterator  i; 
  for(i = iSetAxisJoystickOptions.begin() ; i <iSetAxisJoystickOptions.end();i++){
    if(optionId != i->optionId &&
       joystickId == i->joystickId &&
       axisId == i->buttonOrAxisId){
      dPrint(1,"Removing...");
      remove = true;
      break;      

    }

  }
  if(remove) iSetAxisJoystickOptions.erase(i);

  int result = configOptions.setAxisOption(optionId, joystickId, axisId);
  if(result == JOYSTICKCONTROL_OK){
    iSetAxisJoystickOptions.push_back(JoystickOption(optionId,joystickId,axisId));
  }
  return result;

}               

int JoystickControlComponent::setButtonOption(const int optionId,
                                              const int joystickId,
                                              const int buttonId)
{
  bool remove = false;
  std::vector<JoystickOption>::iterator  i; 
  for(i = iSetButtonJoystickOptions.begin() ; i <iSetButtonJoystickOptions.end();i++){
    if(optionId != i->optionId &&
       joystickId == i->joystickId &&
       buttonId == i->buttonOrAxisId){
      remove = true;
      break;      

    }

  }
  if(remove) iSetButtonJoystickOptions.erase(i);
  int result = configOptions.setButtonOption(optionId, joystickId, buttonId);
  if(result == JOYSTICKCONTROL_OK){
    iSetButtonJoystickOptions.push_back(JoystickOption(optionId,joystickId,buttonId));
  }
  return result;

}
bool JoystickControlComponent::isAxisOptionIdSet(const int aOptionId)
{
  bool result = false;
  std::vector<JoystickOption>::iterator  i; 
  for(i = iSetAxisJoystickOptions.begin() ; i <iSetAxisJoystickOptions.end();i++){
    if(aOptionId == i->optionId){
      result = true;
      break;
    }
  }
  return result;

}
bool JoystickControlComponent::isButtonOptionIdSet(const int aOptionId)
{

 bool result = false;
  std::vector<JoystickOption>::iterator  i; 
  for(i = iSetButtonJoystickOptions.begin() ; i <iSetButtonJoystickOptions.end();i++){
    if(aOptionId == i->optionId){
      result = true;
      break;
    }
  }
  return result;
}
